<?
/*
 +++ VALIDATOR CLASS +++
 
  + Validation Options +
    * required
    * minimumLength
    * maximumLength
    * equalsField
    * telephone
    * email
    * fixedLength
    * numeric
    * currency
    * url
    * fileType
    * fileSize
    * date
*/
class validator extends errorHandler{

	public $validationScript="gravy_validator.js";
	public $outputedValidationScript=false;
	
	public $validationError=false;
	public $isValid=true;
	public $isFatal=false;
	
	public $options=array();
	
	public $validationErrors=array();
	
	public $processErrors=array();
	
	public $validationRules = array();
	
	public function __construct(){
		parent::__construct();
		$this->validationScript=GRAVY_JS_ROOT.$this->validationScript;
	}

	/*
	** Adds a form to the validation dictionary.
	*/
	public function addForm($formName){
		if(!array_key_exists($formName,$this->validationRules)){
			$this->validationRules[$formName]=array();
		}
		if(!array_key_exists($formName,$this->options)){
			$this->options[$formName]=array();
		}
		$this->options[$formName]["clientSideValidation"]=true;
		return($formName);
	}

	/*
	** Adds a rule to the validation dictionary.
	*/
	public function addRule($formName,$field){
		if(is_array($field)){
			if(!array_key_exists($formName,$this->validationRules)){
				$this->validationRules[$formName]=array();
			}
			$this->validationRules[$formName][]=$field;
		}
	}

	/*
	** Removes a rule from the validation dictionary.
	*/
	public function removeRule($formName,$fieldName){
		$result=false;
		if(array_key_exists($formName,$this->validationRules)){
			for($c=0;$c<count($this->validationRules[$formName]);$c++){
				if($this->validationRules[$formName][$c]["fieldName"]==$fieldName){
					array_splice($this->validationRules[$formName],$c,1);
					$c=count($this->validationRules[$formName]);
					$result=true;
				}
			}
		}
		return($result);
	}
	
	/*
	** Adds a group of rule to the validation dictionary.
	*/
	public function addRules($formName,$fields){
		if(is_array($fields)){
			$keys=array_keys($fields);
			for($c=0;$c<count($keys);$c++){
				$key=$keys[$c];
				$value=$fields[$key];
				$this->addRule($formName,$value);
			}
		}
	}

	/*
	** Set's an option for the form's validation processing.
	*/
	public function setFormOption($formName,$key,$value){
		if(!array_key_exists($formName,$this->options)){
			$this->options[$formName]=array();
		}
		$this->options[$formName][$key]=$value;
	}

	/*
	** Change a forms field's rule.
	*/
	public function changeRule($formName,$field,$rule,$option,$value){
		if(array_key_exists($formName,$this->validationRules)){
			for($c=0;$c<count($this->validationRules[$formName]);$c++){
				if($field==$this->validationRules[$formName][$c]["fieldName"]){
					if(array_key_exists($rule,$this->validationRules[$formName][$c]["validate"])){
						$this->validationRules[$formName][$c]["validate"][$rule][$option]=$value;
					}
				}
			}
		}
	}
	
	/*
	** Output the Client-Side Validation Script.
	*/
	public function outputValidationScript($formName=NULL,$caching=true){
		if($this->options[$formName]["clientSideValidation"]){
			if(!$this->outputedValidationScript){
				$this->outputedValidationScript=true;
				$caching=(!$caching)?"?r=".rand(0,10000):"";
				print('<script type="text/javascript" src="'.$this->validationScript.$caching.'"></script>');
			}
			$v=$formName.'_v';
			#$this->debug("valid",$this->validationRules);
			print('<script type="text/javascript">');
			print('var '.$v.'=new gravy_validator("'.$formName.'");');
			for($c=0;$c<count($this->validationRules[$formName]);$c++){
				$rules=$this->validationRules[$formName][$c];
				#print($v.'.setRequired("'.$rules["fieldName"].'",'.$this->textBool($rules["required"]).');');
				print($v.'.addField("'.$rules["fieldName"].'","'.$rules["displayName"].'","'.$rules["inputType"].'");');
				foreach($rules["validate"] as $validation=>$options){
					$jsOptions="{";
					foreach($options as $option=>$value){
						$jsOptions.="$option:";
						if(is_bool($value)){
							$jsOptions.=$this->textBool($value).",";
						}else if(is_numeric($value)){
							$jsOptions.=intval($value).",";
						}else{
							$jsOptions.="'".str_replace("'","\'",$value)."',";
						}
					}
					$jsOptions=substr($jsOptions,0,strlen($jsOptions)-1)."}";
					#print($v.'.addRule("'.$rules["fieldName"].'","'.$validation.'",'.$jsOptions.');'.chr(13));
					print($v.'.addRule("'.$rules["fieldName"].'","'.$validation.'",'.$jsOptions.');');
				}
			}
			if($this->options[$formName]["validateOnChange"]){
				print($v.'.validateOnChange=true;');
			}
			print($v.'.init();');
			print('</script>');
		}
	}
	
	/*
	** Output a field notification DIV.
	*/
	public function outputValidationNotification($form=NULL,$fieldName=NULL){
		$message="";
		$style=' style="display:none;"';
		if(array_key_exists($form,$this->validationErrors)){
			if(array_key_exists($fieldName,$this->validationErrors[$form])){
				$message=$this->validationErrors[$form][$fieldName];
				$style="";
			}
		}
		print('<div id="'.$form.'_'.$fieldName.'" class="gravyError"'.$style.'>'.$message.'</div>');
	}
	
	/*
	** Outputs general processing errors, generally made by the user.
	** Return boolean.
	*/
	public function outputProcessErrors($form=NULL,$title=NULL,$divId=NULL){
		$result=false;
		$message="";
		if(array_key_exists($form,$this->processErrors)){
			if($title!==NULL){
				if(array_key_exists($title,$this->processErrors[$form])){
					if(strlen($title)>0){
						$message.="<h1>$title</h1>";
					}
					$errors=$this->processErrors[$form][$title];
					if(count($errors)>0){
						$message.="<ol>";
						for($c=0;$c<count($errors);$c++){
							$message.="<li>".$errors[$c]."</li>";
						}
						$message.="</ol>";
					}
				}
			}else{
				foreach($this->processErrors[$form] as $title=>$errors){
					if(strlen($title)>0){
						$message.="<h1>$title</h1>";
					}
					if(count($errors)>0){
						$message.="<ol>";
						for($c=0;$c<count($errors);$c++){
							$message.="<li>".$errors[$c]."</li>";
						}
						$message.="</ol>";
					}
				}
			}
			if(strlen($message)>0){
				$divId=($divId===NULL)?"gravyProcessError":$divId;
				print('<div id="'.$divId.'" class="gravyProcessError">'.$message.'</div>');
			}
			$result=true;
		}
		return($result);
	}
	
	/*
	** Outputs general processing errors, generally made by the user.
	*/
	public function addProcessError($form=NULL,$error="",$title="",$isFatal=false){
		if($isFatal){
			$this->isFatal=true;
		}
		if(!array_key_exists($form,$this->processErrors)){
			$this->processErrors[$form]=array();
		}
		if(!array_key_exists($title,$this->processErrors[$form])){
			$this->processErrors[$form][$title]=array();
		}
		$this->processErrors[$form][$title][]=$error;
		$this->isError=true;
		$this->log("gravy->validator->processingError(form=$form,error=$error,title=$title,isFatal=$isFatal);");
	}
	
	/*
	** Returns the text "true" or "false" based on the value supplied.
	*/
	public function textBool($val=NULL){
		return(($val!==NULL&&$val)?"true":"false");
	}
	
	/*
	** Validates the fields based on the rules supplied.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function validate($form){
		$result=true;
		if(is_array($this->validationRules[$form])){
			for($c=0;$c<count($this->validationRules[$form]);$c++){
				$rule=$this->validationRules[$form][$c];
				$fieldName=$rule["fieldName"];
				$displayName=$rule["displayName"];
				$value=$_REQUEST[$fieldName];
				if(is_array($rule["validate"])){
					foreach($rule["validate"] as $check=>$options){
						if(method_exists($this,$check)){
							if(!$this->{$check}($form,$fieldName,$displayName,$value,$options)){
								$result=false;
								break;
							}
						}else{
							#$this->debug("Error, method doesnt exist: validator->".$check);
						}
					}
				}
			}
		}
		return($result);
	}
	
	/*
	** Add a validation error to the validation dictionary.
	** 
	** Returns void.
	*/
	public function addValidationError($form,$fieldName,$displayName,$message=""){
		$result=true;
		$this->validationError=true;
		$this->isValid=false;
		$this->isError=true;
		$message=str_replace("%displayName%",$displayName,$message);
		if(strlen($message)<1){
			$message="This field contains an error.";
		}
		if(!array_key_exists($form,$this->validationRules)){
			$this->validationErrors[$form]=array();
		}
		#$this->debug("Adding Error: $message");
		$this->validationErrors[$form][$fieldName]=$message;
		return($result);
	}

	/*
	** Check if a field has a value.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function required($form,$fieldName,$displayName,$value,$options){
		$result=true;
		if($options["value"]!==FALSE&&$options["value"]!=FALSE&&$options["value"]!=0){
			if(!isset($value)||strlen((string)$value)<1){
				if($options["type"]!="file"||($options["type"]=="file"&&!$_FILES[$fieldName]&&strlen($_FILES[$fieldName]["name"])<1)){
					$message=(strlen($options["errorMessage"])<1)?"A value must be entered in the \"$displayName\" field.":$options["errorMessage"];
					$this->addValidationError($form,$fieldName,$displayName,$message);
					$result=false;
				}
			}
		}
		return($result);
	}

	/*
	** Check if a field value has a minimum of X characters.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function minimumLength($form,$fieldName,$displayName,$value,$options){
		$result=true;
		$options["value"]=intval($options["value"]);
		if(strlen((string)$value)<$options["value"]&&strlen($value)>0){
			$message=(strlen($options["errorMessage"])<1)?"There must be more than ".$options["value"]." characters entered in the \"$displayName\" field.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}
		return($result);
	}

	/*
	** Check if a field value has a maximum of X characters.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function maximumLength($form,$fieldName,$displayName,$value,$options){
		$result=true;
		$options["value"]=intval($options["value"]);
		if(strlen((string)$value)>$options["value"]&&strlen($value)>0){
			$message=(strlen($options["errorMessage"])<1)?"There must be no more than ".$options["value"]." characters entered in the \"$displayName\" field.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}
		return($result);
	}

	/*
	** Check if a field value is equal to that of another field.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function equalsField($form,$fieldName,$displayName,$value,$options){
		$result=true;
		$options["value"]=(string)$options["value"];
		$displayName2=(string)$options["displayName"];
		if((string)$value!=$_REQUEST[$options["value"]]){
			$message=(strlen($options["errorMessage"])<1)?"The value in $displayName must match the value in the $displayName2 field.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}
		return($result);
	}

	/*
	** Check if a value is in the right format to be a telephone number.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function telephone($form,$fieldName,$displayName,$value,$options){
		$result=true;
		$removeInvalidChars=(bool)$options["removeInvalidChars"];
		$re="/[^0-9\.\(\)\-\+\s]/";
		if(preg_match($re,$value)>0&&strlen($value)>0){
			$message=(strlen($options["errorMessage"])<1)?"Please enter a valid telephone number in the \"$displayName\" field.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}
		return($result);
	}

	/*
	** Check if a value is in an email format.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function email($form,$fieldName,$displayName,$value,$options){
		$result=true;
		$re="/^[\w-\.]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]{2,8}$/";
		$re_res=preg_match($re,$value);
		if(($re_res<1||$re_res===FALSE)&&strlen($value)>0){
			$message=(strlen($options["errorMessage"])<1)?"Please enter a valid Email Address number in the \"$displayName\" field.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}
		return($result);
	}

	/*
	** Ensure a value is exactly X characters long.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function fixedLength($form,$fieldName,$displayName,$value,$options){
		$result=true;
		$tmp=str_replace(" ","",$value);
		$length=(int)$options["value"];
		if(strlen($tmp)!=$length&&strlen($value)>0){
			$message=(strlen($options["errorMessage"])<1)?"The %displayName% must be 11 characters long.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}
		return($result);
	}

	/*
	** Ensure a value only contains numbers.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function numeric($form,$fieldName,$displayName,$value,$options){
		$result=true;
		$tmp=(int)$options["value"];
		$value=str_replace(" ","",$value);
		if($tmp&&!is_numeric($value)&&strlen($value)>0){
			$message=(strlen($options["errorMessage"])<1)?"The %displayName% can only contain numbers.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}elseif(!$tmp&&is_numeric($value)&&strlen($value)>0){
			$message=(strlen($options["errorMessage"])<1)?"The %displayName% can not contain any numbers.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}
		return($result);
	}

	/*
	** Ensure a value is a valid currency (i.e. currency sign, numbers, commas, 1 decimal point).
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function currency($form,$fieldName,$displayName,$value,$options){
		$result=true;
		#$value=str_replace(" ","",$value);
		#$re="^[^\d]{,5}\d*(\.\d+){1}\$";
		$value=preg_replace("/[^\\d\\.]/i","",$value);
		if(strlen($value)>0){
			if(!is_numeric($value)){
				$this->debug("bad currency",$value);
				$message=(strlen($options["errorMessage"])<1)?"Please enter a valid %displayName%.":$options["errorMessage"];
				$this->addValidationError($form,$fieldName,$displayName,$message);
				$result=false;
			}
		}
		return($result);
	}

	/*
	** Check if a value is in an email format.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function url($form,$fieldName,$displayName,$value,$options){
		$result=true;
		if(strlen($value)>0){
			if(strtolower(substr($value,0,6))!="ftp://"&&strtolower(substr($value,0,7))!="http://"&&strtolower(substr($value,0,8))!="https://"){
				$value="http://$value";
			}
			$urlregex="^(https?|ftp)\:\/\/([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*(\:[0-9]{2,5})?(\/([a-z0-9+\$_-]\.?)+)*\/?(\?[a-z+&\$_.-][a-z0-9;:@/&%=+\$_.-]*)?(#[a-z_.-][a-z0-9+\$_.-]*)?\$";
			if(!eregi($urlregex,$value)&&strlen($value)>0){
				$message=(strlen($options["errorMessage"])<1)?"Please enter a valid %displayName%.":$options["errorMessage"];
				$this->addValidationError($form,$fieldName,$displayName,$message);
				$result=false;
			}
		}
		return($result);
	}

	/*
	** Check if the file type is correct.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function fileType($form,$fieldName,$displayName,$value,$options){
		$result=true;
		if($_FILES[$fieldName]){
			$fileTypes=split(",",$options["value"]);
			if(is_array($fileTypes)&&strlen($_FILES[$fieldName]["name"])>0){
				$found=false;
				for($c=0;$c<count($fileTypes);$c++){
					$this->debug("Testing Validator1", $_FILES[$fieldName]["type"]);
					$this->debug("Testing Validator2", $fileTypes[$c]);
					if(strstr(strtolower($_FILES[$fieldName]["type"]),strtolower($fileTypes[$c]))){
						$found=true;
						break;
					}
				}
				if(!$found){
					$message=(strlen($options["errorMessage"])<1)?"The %displayName% file must be one of the following file formats ".$options["value"].".":$options["errorMessage"];
					$this->addValidationError($form,$fieldName,$displayName,$message);
					$result=false;
				}
			}
		}
		return($result);
	}

	/*
	** Check if the file type is correct.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function fileSize($form,$fieldName,$displayName,$value,$options){
		$result=true;
		if($_FILES[$fieldName]){
			$fileSize=$options["value"];
			if($_FILES[$fieldName]["size"]>$fileSize){
				$message=(strlen($options["errorMessage"])<1)?"The file size of the %displayName% must not exceed ".number_format($fileSize,0)." bytes.":$options["errorMessage"];
				$this->addValidationError($form,$fieldName,$displayName,$message);
				$result=false;
			}
		}
		return($result);
	}

	/*
	** Check if the date is correct.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function date($form,$fieldName,$displayName,$value,$options){
		$result=true;
		if(strlen($value)>0){
			$date1=@strtotime($value);
			$date2=@strtotime(formatter::auDateToUniversal($value,false));
			if(($date1===FALSE||$date1==-1) && ($date2===FALSE||$date2==-1)){
				$message=(strlen($options["errorMessage"])<1)?"The date entered is not valid.":$options["errorMessage"];
				$this->addValidationError($form,$fieldName,$displayName,$message);
				$result=false;
			}
		}
		return($result);
	}

	/*
	** Check for invalid characters.
	** 
	** Returns true if all data is valid and false if it's not.
	*/
	public function regexpMatch($form,$fieldName,$displayName,$value,$options){
		$result=true;
		$re=$options["value"];
		$re_res=preg_match($re,$value);
		if($re_res===TRUE&&strlen($value)>0&&strlen($options["value"])>0){
			$message=(strlen($options["errorMessage"])<1)?"The field \"$displayName\" was not in the correct format.":$options["errorMessage"];
			$this->addValidationError($form,$fieldName,$displayName,$message);
			$result=false;
		}
		return($result);
	}
};
?>